Skip to content

feat(opencode): add killswitch indicators to TUI sidebar#39

Open
iceteaSA wants to merge 2 commits into
cortexkit:mainfrom
iceteaSA:feat/killswitch-sidebar
Open

feat(opencode): add killswitch indicators to TUI sidebar#39
iceteaSA wants to merge 2 commits into
cortexkit:mainfrom
iceteaSA:feat/killswitch-sidebar

Conversation

@iceteaSA
Copy link
Copy Markdown
Contributor

@iceteaSA iceteaSA commented May 21, 2026

Dependency: Requires #35 (killswitch) to be merged first. Rebased onto upstream/main (v1.5.0) now that #37 (TUI sidebar) is merged.

Adds killswitch awareness to the TUI sidebar widget (the integration of #35 + the merged sidebar):

  • blocked (red) state for killswitch-killed main and fallback accounts.
  • Quota-backoff and refresh-backoff state display.
  • A Killswitch row in the Health section and inclusion in the LIMITED badge.
  • writeSidebarState() computes killswitch policy for every account using fresh, token-aware QuotaManager reads (never the stale request-start snapshot).

Files:

  • packages/opencode/src/sidebar-state.ts — adds killed + backoff fields
  • packages/opencode/src/tui.tsx — blocked indicator + kill status section
  • packages/opencode/src/index.ts — killswitch-aware sidebar state

Summary by cubic

Adds a killswitch that hard-blocks accounts when quota drops below configured thresholds and integrates it into routing and the TUI; /claude-killswitch manages thresholds. Killed accounts never route, and when none are routable we return a synthetic 429 with retry-after from the earliest reset.

  • New Features

    • Killswitch: eager main/fallback quota refresh before evaluation, unified getRoutableFallbackAccounts filter across all routes, non‑replayable bodies never fall through, and a hard 429 with retry-after from the earliest reset (+60s buffer).
    • Sidebar: blocked status word and red ⊘ indicator, Health row listing killswitch‑blocked accounts, killed flags for main/fallbacks, resolveActiveAccount() correction, and background fallback storage changes re‑render via onFallbackStorageChanged.
    • Commands: /claude-killswitch (status, on, off, set main|all|<id>:<5h>,<1w>); changes persist via @cortexkit/anthropic-auth-core.
  • Bug Fixes

    • Token‑aware reads for fail‑closed and killswitch checks prevent stale quota from previous accounts from affecting decisions.
    • Restored process‑scoped request counter so every‑N quota refreshes trigger correctly; keep active fallback selection after async refresh and keep the sidebar visible when data clears while collapsed.

Written for commit 5eb08e5. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR wires killswitch awareness into the OpenCode TUI sidebar: killed flags on main and fallback accounts, a collapsed header with active-account quota and a red ⊘ blocked indicator, a Killswitch row in the Health section, and a /claude-killswitch command for managing per-account hard-block thresholds. The core routing gate in index.ts addresses the previously flagged issues around stale quota on first request, non-replayable body handling, and the disagreement between allFallbacksKilled and survivingFallbacks.

  • packages/core/src/accounts.ts + killswitch.ts: killswitchPassesPolicy correctly defers the unknown-window decision so a present below-threshold window always blocks even when the other window is missing; killswitchRetryAfterSeconds picks the earliest reset across all accounts and adds a 60-second buffer.
  • packages/opencode/src/index.ts: Eager quota refresh before killswitch evaluation, token-aware mainQuota re-read after refresh, getRoutableFallbackAccounts as the single source of truth for all fallback-selection paths, and non-replayable bodies treated as having zero survivors.
  • packages/opencode/src/tui.tsx: Collapsible header (click to toggle), collapsed view showing active-account 5h usage, and the Killswitch StatRow — though this row is currently placed outside the <Show when={degraded()}> Health block rather than inside it.

Confidence Score: 4/5

The killswitch gate is safe to merge — the three routing bugs identified in earlier review threads are addressed and backed by targeted integration tests.

The routing logic in index.ts is complex and the fixes look correct. The one remaining finding — the Killswitch StatRow placed outside the Health Show block — is purely structural and does not change the visual output today, but is a latent maintainability concern.

packages/opencode/src/tui.tsx — Killswitch StatRow placement outside the degraded Show block.

Important Files Changed

Filename Overview
packages/core/src/accounts.ts Adds KillswitchConfig/KillswitchThresholds types, killswitchPassesPolicy with correct fail-closed/fail-open handling for missing quota windows, killswitchRetryAfterSeconds, and onFallbackStorageChanged callback to FallbackAccountManager. Logic is sound and well-tested.
packages/core/src/killswitch.ts New file implementing the /claude-killswitch command: parsing, status display, and on/off/set actions. Logic is correct, the 'all' alias correctly sets main and per-account thresholds simultaneously.
packages/opencode/src/index.ts Core killswitch gate in the fetch path: eager quota refresh, token-aware mainQuota re-read, non-replayable body treated as no survivors, and getRoutableFallbackAccounts unifying the killswitch filter across all routes. Addresses previously flagged issues from PR threads.
packages/opencode/src/sidebar-state.ts Adds killed field to SidebarState, updates DEFAULT_SIDEBAR_STATE, and introduces resolveActiveAccount helper for collapsed view. Clean and well-tested.
packages/opencode/src/tui.tsx Adds collapsible header, collapsed view with active-account quota row, blocked indicator (⊘), and killswitch Health section. The Killswitch StatRow is placed outside the Health block — it works because degraded() includes killedNames().length > 0, but the nesting is structurally inconsistent.
packages/opencode/src/tests/index.test.ts Adds comprehensive killswitch fetch gate tests covering non-replayable bodies, fallback-first routing, fail-closed unknown quota, sidebar active-account correction, and background refresh sidebar updates.
packages/opencode/src/tests/killswitch.test.ts New file with thorough unit tests for killswitchPassesPolicy, killswitchRetryAfterSeconds, config helpers, setKillswitchPersistent, and executeKillswitchCommand. Edge cases for unknown quota windows and partial quota snapshots are covered.
packages/opencode/src/tests/sidebar-state.test.ts New file covering resolveActiveAccount for all activeId scenarios including disabled fallbacks, unmatched ids, and killed flag propagation. Complete coverage.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Fetch request arrives] --> B{failClosedOnUnknownQuota\n+ no quota + backed off?}
    B -- Yes --> C[Return synthetic 429\nwith quota backoff retry-after]
    B -- No --> D{killswitch enabled?}
    D -- No --> K[Normal routing]
    D -- Yes --> E{needsRefresh?}
    E -- Yes --> F[Eager refresh:\nrefreshMain + refreshAllFallbacks]
    F --> G[Re-read mainQuota\ntoken-aware]
    E -- No --> G
    G --> H{killswitchPassesPolicy\nmainQuota?}
    H -- Pass --> K
    H -- Fail: main killed --> I{isReplayableRequest?}
    I -- Yes --> J[getRoutableFallbackAccounts\nusable ∩ killswitch-pass]
    I -- No --> N[survivingFallbacks = empty]
    J --> L{surviving\nfallbacks > 0?}
    N --> M
    L -- Yes --> M2[tryUsableFallbackAccounts\nhard block: never fall through to main]
    M2 --> M2r{fallbackResponse?}
    M2r -- Yes --> M2s[Return fallback response]
    M2r -- No --> M[Return synthetic 429\nkillswitchRetryAfterSeconds]
    L -- No --> M
Loading

Reviews (11): Last reviewed commit: "docs(opencode): clarify version path lay..." | Re-trigger Greptile

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 16 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Fix all with cubic | Re-trigger cubic

Comment thread packages/core/src/accounts.ts Outdated
Comment thread packages/opencode/src/tui.tsx Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 2 times, most recently from 84f44e5 to 583586a Compare May 21, 2026 20:20
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 21, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 4 times, most recently from 09b34c3 to 8339edc Compare May 22, 2026 17:08
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 10 times, most recently from e86d9d3 to a8d6404 Compare June 3, 2026 18:24
Comment thread packages/opencode/src/index.ts Outdated
Comment thread packages/opencode/src/index.ts Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch from a8d6404 to ecffae9 Compare June 3, 2026 18:58
Comment thread packages/opencode/src/index.ts Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 4 times, most recently from 4988ae1 to 78b02b4 Compare June 3, 2026 19:53
Comment thread packages/opencode/src/index.ts Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 4 times, most recently from eaf91c9 to 3147ad3 Compare June 3, 2026 21:03
@iceteaSA
Copy link
Copy Markdown
Contributor Author

iceteaSA commented Jun 4, 2026

Updated: this branch now carries the sidebar quota fix (#57) and the collapsible sidebar (#58), and integrates the collapsed view with the killswitch indicators — the collapsed active-account row shows a red ⊘ (instead of the usage dot) when that account is killswitch-blocked; resolveActiveAccount exposes killed. #57 and #58's commits appear here until they merge. Merge order: #57#58 → this PR.

iceteaSA added 2 commits June 4, 2026 10:04
…reshold

Self-review fixes folded in:
- Token-aware fail-closed read: const mainQuota = quotaManager.getMain(auth.access)
  so a previous main account's cached quota can't satisfy the fail-closed check
  or feed the killswitch eval after a main-account switch.
- Removed a stray inner 'let sessionRequestCount = 0' + unconditional increment
  that shadowed the process-scoped counter, which had left the active-route
  fallback every-N refresh reading a never-incremented counter.
Layer killswitch awareness onto the restyled sidebar (killed state in
SidebarState + writeSidebarState via killswitchPassesPolicy, blocked status
word, Killswitch health row, degraded/LIMITED inclusion).

Also restores the process-scoped 'let sessionRequestCount' (a prior cascade
had flipped it to const, which left the active-route fallback every-N refresh
reading a never-incremented counter).
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch from c73b37e to 5eb08e5 Compare June 4, 2026 08:08
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iceteaSA has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant